#include "ImageProcessor.h"
#include <QDebug>
#include <QThread>
#include <QCoreApplication>
#include <QtMath>

ImageProcessor::ImageProcessor(int width, int height, int fps, QObject *parent)
    : QObject(parent), m_codec(nullptr), m_codecCtx(nullptr),
      m_frame(nullptr), m_packet(nullptr), m_frameCount(0)
{
    m_width = width;
    m_height = height;
    m_fps = fps;
}

ImageProcessor::~ImageProcessor()
{
    // 发送空帧，确保编码器缓存中的数据被刷新
    if (m_codecCtx) {
        avcodec_send_frame(m_codecCtx, nullptr);
    }
    
    // 释放FFmpeg资源
    if (m_packet) {
        av_packet_free(&m_packet);
        m_packet = nullptr;
    }
    if (m_frame) {
        av_frame_free(&m_frame);
        m_frame = nullptr;
    }
    if (m_codecCtx) {
        avcodec_free_context(&m_codecCtx);
        m_codecCtx = nullptr;
    }
}

bool ImageProcessor::initEncoder()
{
    //QMutexLocker locker(&m_mutex);
    
    // 查找H.264编码器
    m_codec = avcodec_find_encoder(AV_CODEC_ID_H264); //软件编码器
    if (!m_codec) {
        emit errorOccurred("找不到H.264编码器");
        return false;
    }
    
    // 创建编码器上下文
    m_codecCtx = avcodec_alloc_context3(m_codec);
    if (!m_codecCtx) {
        emit errorOccurred("无法分配编码器上下文");
        return false;
    }
    
    // 设置编码器参数
    m_codecCtx->width = m_width;
    m_codecCtx->height = m_height;
    m_codecCtx->time_base = {1, m_fps};
    m_codecCtx->framerate = {m_fps, 1};
    m_codecCtx->gop_size = 10; // 每10帧一个关键帧
    m_codecCtx->max_b_frames = 1;
    m_codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    m_codecCtx->bit_rate = 2000000; // 2Mbps比特率
    
    // 对于实时传输，使用零延迟模式
    if (m_codec->id == AV_CODEC_ID_H264) {
        av_opt_set(m_codecCtx->priv_data, "preset", "ultrafast", 0);
        av_opt_set(m_codecCtx->priv_data, "tune", "zerolatency", 0);
    }
    
    // 打开编码器
    if (avcodec_open2(m_codecCtx, m_codec, nullptr) < 0) {
        emit errorOccurred("无法打开编码器");
        avcodec_free_context(&m_codecCtx);
        m_codecCtx = nullptr;
        return false;
    }
    
    // 初始化帧和包
    m_frame = av_frame_alloc();
    if (!m_frame) {
        emit errorOccurred("无法分配帧");
        avcodec_free_context(&m_codecCtx);
        m_codecCtx = nullptr;
        return false;
    }
    m_frame->format = m_codecCtx->pix_fmt;
    m_frame->width = m_width;
    m_frame->height = m_height;
    
    // 分配帧数据缓冲区
    if (av_frame_get_buffer(m_frame, 0) < 0) {
        emit errorOccurred("无法为帧分配缓冲区");
        av_frame_free(&m_frame);
        avcodec_free_context(&m_codecCtx);
        m_frame = nullptr;
        m_codecCtx = nullptr;
        return false;
    }
    
    m_packet = av_packet_alloc();
    if (!m_packet) {
        emit errorOccurred("无法分配数据包");
        av_frame_free(&m_frame);
        avcodec_free_context(&m_codecCtx);
        m_frame = nullptr;
        m_codecCtx = nullptr;
        return false;
    }
    
    qInfo() << "编码器初始化成功，分辨率:" << m_width << "x" << m_height << "帧率:" << m_fps;
    return true;
}

bool ImageProcessor::initHardwareEncoder_h264_nvenc()
{
    // 查找NVIDIA NVENC H.264硬件编码器
    m_codec = avcodec_find_encoder_by_name("h264_nvenc"); // 注意编码器名称是h264_nvenc而非nvenc_h264
    if (!m_codec) {
        emit errorOccurred("找不到H.264 NVENC硬件编码器");
        return false;
    }

    // 创建编码器上下文
    m_codecCtx = avcodec_alloc_context3(m_codec);
    if (!m_codecCtx) {
        emit errorOccurred("无法分配编码器上下文");
        return false;
    }

    // 设置编码器基本参数
    m_codecCtx->width = m_width;
    m_codecCtx->height = m_height;
    m_codecCtx->time_base = {1, m_fps};
    m_codecCtx->framerate = {m_fps, 1};
    m_codecCtx->gop_size = 10; // 每10帧一个关键帧
    m_codecCtx->max_b_frames = 0; // NVENC在低延迟模式下建议关闭B帧
    m_codecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    m_codecCtx->bit_rate = 2000000; // 2Mbps比特率
    m_codecCtx->rc_buffer_size = m_codecCtx->bit_rate * 2; // 缓冲区大小设为比特率的2倍
    m_codecCtx->rc_max_rate = m_codecCtx->bit_rate * 1.5;  // 最大比特率限制
    m_codecCtx->rc_min_rate = m_codecCtx->bit_rate * 0.5;  // 最小比特率限制

    // NVENC特定参数配置
    // 低延迟高质量预设，适合实时场景
    av_opt_set(m_codecCtx->priv_data, "preset", "llhq", 0);
    // 零延迟调优
    av_opt_set(m_codecCtx->priv_data, "tune", "zerolatency", 0);
    // 关闭前瞻编码，减少延迟
    av_opt_set_int(m_codecCtx->priv_data, "rc-lookahead", 0, 0);
    // 设置GPU设备ID，多GPU场景下可指定(0为默认)
    av_opt_set_int(m_codecCtx->priv_data, "device", 0, 0);
    // 启用硬件加速的色彩空间转换
    av_opt_set_int(m_codecCtx->priv_data, "hwaccel", 1, 0);

    // 打开编码器
    AVDictionary *opts = nullptr;
    // 对于笔记本双显卡，强制使用NVIDIA GPU
    av_dict_set(&opts, "init_hw_device", "cuda=nv", 0);
    av_dict_set(&opts, "hwaccel_device", "nv", 0);
    av_dict_set(&opts, "vcodec", "h264_nvenc", 0);

    int ret = avcodec_open2(m_codecCtx, m_codec, &opts);
    av_dict_free(&opts); // 释放字典资源

    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        emit errorOccurred(QString("无法打开NVENC编码器: %1").arg(errbuf));
        avcodec_free_context(&m_codecCtx);
        m_codecCtx = nullptr;
        return false;
    }

    // 初始化帧和包
    m_frame = av_frame_alloc();
    if (!m_frame) {
        emit errorOccurred("无法分配帧");
        avcodec_free_context(&m_codecCtx);
        m_codecCtx = nullptr;
        return false;
    }
    m_frame->format = m_codecCtx->pix_fmt;
    m_frame->width = m_width;
    m_frame->height = m_height;

    // 分配帧数据缓冲区
    if (av_frame_get_buffer(m_frame, 0) < 0) {
        emit errorOccurred("无法为帧分配缓冲区");
        av_frame_free(&m_frame);
        avcodec_free_context(&m_codecCtx);
        m_frame = nullptr;
        m_codecCtx = nullptr;
        return false;
    }

    m_packet = av_packet_alloc();
    if (!m_packet) {
        emit errorOccurred("无法分配数据包");
        av_frame_free(&m_frame);
        avcodec_free_context(&m_codecCtx);
        m_frame = nullptr;
        m_codecCtx = nullptr;
        return false;
    }

    qInfo() << "NVENC硬件编码器初始化成功，分辨率:" << m_width << "x" << m_height
            << "帧率:" << m_fps << "编码器:" << m_codec->name;
    return true;
}

bool ImageProcessor::initHardwareEncoder_h264_vaapi()
{
    // 查找VAAPI H.264硬件编码器（EncodeName应传入"h264_vaapi"）
    m_codec = avcodec_find_encoder_by_name("h264_vaapi");
    if (!m_codec) {
        emit errorOccurred("找不到H.264 VAAPI硬件编码器");
        return false;
    }

    // 创建编码器上下文
    m_codecCtx = avcodec_alloc_context3(m_codec);
    if (!m_codecCtx) {
        emit errorOccurred("无法分配编码器上下文");
        return false;
    }

    // --------------------------
    // 1. 基础编码参数（部分与NVENC通用）
    // --------------------------
    m_codecCtx->width = m_width;
    m_codecCtx->height = m_height;
    m_codecCtx->time_base = {1, m_fps};       // 时间基（分母为帧率）
    m_codecCtx->framerate = {m_fps, 1};       // 帧率
    m_codecCtx->gop_size = 10;                // 每10帧一个关键帧
    m_codecCtx->max_b_frames = 0;             // 低延迟场景关闭B帧
    m_codecCtx->bit_rate = 2000000;           // 目标比特率（2Mbps）
    m_codecCtx->rc_buffer_size = m_codecCtx->bit_rate * 2;  // 缓冲区大小
    m_codecCtx->rc_max_rate = m_codecCtx->bit_rate * 1.5;   // 最大比特率
    m_codecCtx->rc_min_rate = m_codecCtx->bit_rate * 0.5;   // 最小比特率
    m_codecCtx->pix_fmt = AV_PIX_FMT_VAAPI;   // VAAPI使用硬件像素格式


    // --------------------------
    // 2. 初始化VAAPI硬件设备
    // --------------------------
    AVBufferRef *hw_device_ctx = nullptr;
    // 设备路径：通常Intel为"/dev/dri/renderD128"，AMD可能不同（需根据实际环境调整）
    int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI,
                                     "/dev/dri/renderD128", nullptr, 0);
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        emit errorOccurred(QString("VAAPI硬件设备初始化失败: %1").arg(errbuf));
        avcodec_free_context(&m_codecCtx);
        return false;
    }
    // 将硬件设备上下文关联到编码器
    m_codecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx);


    // --------------------------
    // 3. 创建VAAPI硬件帧上下文（管理硬件帧缓冲区）
    // --------------------------
    AVBufferRef *hw_frames_ctx = nullptr;
    AVHWFramesContext *frames_ctx = nullptr;

    // 分配硬件帧上下文
    hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx);
    if (!hw_frames_ctx) {
        emit errorOccurred("无法分配VAAPI硬件帧上下文");
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }

    // 配置硬件帧参数
    frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data;
    frames_ctx->format = AV_PIX_FMT_VAAPI;    // 硬件像素格式
    frames_ctx->width = m_width;
    frames_ctx->height = m_height;
    frames_ctx->sw_format = AV_PIX_FMT_YUV420P;  // 对应的软件格式（用于数据上传）
    frames_ctx->initial_pool_size = 16;       // 帧池大小（根据需求调整）

    // 初始化硬件帧上下文
    ret = av_hwframe_ctx_init(hw_frames_ctx);
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        emit errorOccurred(QString("VAAPI硬件帧上下文初始化失败: %1").arg(errbuf));
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }


    // --------------------------
    // 4. VAAPI私有参数配置（与NVENC不同）
    // --------------------------
    // 预设：fast/medium/slow（速度与质量权衡）
    av_opt_set(m_codecCtx->priv_data, "preset", "fast", 0);
    // 码率控制模式：cqp（恒定质量）/cbr（恒定比特率）/vbr（可变比特率）
    av_opt_set(m_codecCtx->priv_data, "rc-mode", "vbr", 0);
    // 若使用cqp模式，可设置质量参数（0-51，值越小质量越高）
    // av_opt_set_int(m_codecCtx->priv_data, "qp", 25, 0);


    // --------------------------
    // 5. 打开编码器
    // --------------------------
    AVDictionary *opts = nullptr;
    ret = avcodec_open2(m_codecCtx, m_codec, &opts);
    av_dict_free(&opts);

    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        emit errorOccurred(QString("无法打开VAAPI编码器: %1").arg(errbuf));
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }


    // --------------------------
    // 6. 初始化硬件帧（使用VAAPI帧缓冲区）
    // --------------------------
    m_frame = av_frame_alloc();
    if (!m_frame) {
        emit errorOccurred("无法分配帧");
        // 释放资源
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }

    // 关联硬件帧上下文到帧
    m_frame->hw_frames_ctx = av_buffer_ref(hw_frames_ctx);
    if (!m_frame->hw_frames_ctx) {
        emit errorOccurred("无法关联硬件帧上下文");
        av_frame_free(&m_frame);
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }

    // 分配硬件帧缓冲区（VAAPI专用）
    ret = av_hwframe_get_buffer(m_frame->hw_frames_ctx, m_frame, 0);
    if (ret < 0) {
        char errbuf[AV_ERROR_MAX_STRING_SIZE];
        av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
        emit errorOccurred(QString("无法为VAAPI帧分配缓冲区: %1").arg(errbuf));
        av_frame_free(&m_frame);
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }


    // --------------------------
    // 7. 初始化数据包
    // --------------------------
    m_packet = av_packet_alloc();
    if (!m_packet) {
        emit errorOccurred("无法分配数据包");
        // 释放资源
        av_frame_free(&m_frame);
        av_buffer_unref(&hw_frames_ctx);
        av_buffer_unref(&hw_device_ctx);
        avcodec_free_context(&m_codecCtx);
        return false;
    }


    qInfo() << "VAAPI硬件编码器初始化成功，分辨率:" << m_width << "x" << m_height
            << "帧率:" << m_fps << "编码器:" << m_codec->name;
    return true;
}


bool ImageProcessor::convertToYUV420P(const QByteArray &grayData, AVFrame *frame)
{
    // 16位灰度图转8位并填充到YUV420P的Y分量
    const uint16_t *src = reinterpret_cast<const uint16_t*>(grayData.constData());
    uint8_t *yPlane = frame->data[0];
    int yStride = frame->linesize[0];
    
    for (int y = 0; y < frame->height; y++) {
        for (int x = 0; x < frame->width; x++) {
            // 16位转8位：右移8位或根据实际范围调整
            uint8_t val = static_cast<uint8_t>(src[y * frame->width + x] >> 8);
            yPlane[y * yStride + x] = val;
        }
    }
    
    // 填充UV分量为128（灰度图，UV分量为中间值）
    uint8_t *uPlane = frame->data[1];
    uint8_t *vPlane = frame->data[2];
    int uStride = frame->linesize[1];
    int vStride = frame->linesize[2];
    int uvHeight = frame->height / 2;
    int uvWidth = frame->width / 2;
    
    for (int y = 0; y < uvHeight; y++) {
        memset(uPlane + y * uStride, 128, uvWidth);
        memset(vPlane + y * vStride, 128, uvWidth);
    }
    
    return true;
}

//第二种16bit转换方式
void ImageProcessor::gray16To8bit(const QByteArray &grayData, AVFrame *frame)
{
    const ushort *pRawData2 = reinterpret_cast<const ushort*>(grayData.data());
    uchar *pRawData3 = (uchar*)calloc(frame->width * frame->height, sizeof(uchar));
    if (!pRawData3) {
        qWarning("内存分配失败");
        return;
    }

    // 计算16位数据的平均值
    uint64_t sum = 0;  // 使用64位避免溢出
    const int totalPixels = frame->width * frame->height;
    for (int i = 0; i < totalPixels; ++i) {
       sum += pRawData2[i];
    }
    uint16_t aver = static_cast<uint16_t>(sum / totalPixels);

    // 计算动态范围，增加边界检查（防止超出16位范围）
    // 将 aver - 400 显式转换为 uint16_t 类型
    uint16_t minVal = qMax(static_cast<uint16_t>(0), static_cast<uint16_t>(aver - 400));
    uint16_t maxVal = qMin(static_cast<uint16_t>(65535), static_cast<uint16_t>(aver + 400));

    // 处理强度为0的特殊情况，避免除零错误
    float strength = maxVal - minVal;
    if (strength <= 0) {
       strength = 1.0f;  // 避免除零，所有像素设为中间值
    }

    // 16位到8位的映射转换
    for (int i = 0; i < totalPixels; ++i) {
       //  clamping（限制在[minVal, maxVal]范围内）
       uint16_t clamped = pRawData2[i];
       if (clamped < minVal) clamped = minVal;
       else if (clamped > maxVal) clamped = maxVal;

       // 映射到0-255范围
       pRawData3[i] = static_cast<uchar>((clamped - minVal) * 255.0f / strength);
    }

    // 将8位数据填充到AVFrame
    // 假设AVFrame已经正确分配，像素格式为AV_PIX_FMT_GRAY8
    int lineSize = frame->linesize[0];  // 获取每行字节数
    uchar *frameData = frame->data[0];

    for (int y = 0; y < frame->height; ++y) {
       // 逐行拷贝数据（考虑可能的内存对齐导致的行间距）
       memcpy(frameData + y * lineSize,
              pRawData3 + y * frame->width,
              640 * sizeof(uchar));
    }

    // 释放临时内存
    free(pRawData3);
}

bool ImageProcessor::encodeFrame(AVFrame *frame, QByteArray &outputData)
{
    //QMutexLocker locker(&m_mutex);
    if (!m_codecCtx || !frame || !m_packet) {
        return false;
    }
    
    // 设置帧序号和时间戳
    frame->pts = m_frameCount++;
    
    // 发送帧到编码器
    int ret = avcodec_send_frame(m_codecCtx, frame);
    if (ret < 0) {
        emit errorOccurred("发送帧到编码器失败");
        return false;
    }
    
    // 接收编码后的数据包
    ret = avcodec_receive_packet(m_codecCtx, m_packet);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        return false; // 暂时没有输出，或已完成
    } else if (ret < 0) {
        emit errorOccurred("编码帧失败");
        return false;
    }
    
    // 将编码数据存入QByteArray
    outputData.append(reinterpret_cast<const char*>(m_packet->data), m_packet->size);
    
    // 释放数据包数据（但保留数据包结构）
    av_packet_unref(m_packet);
    return true;
}

void ImageProcessor::onProcessImageData(const QByteArray &data)
{
    qDebug() << "进入ffmpeg处理中";

    // 检查是否在工作线程中运行
//    if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
//        emit errorOccurred("图像处理不在工作线程中运行");
//        //qDebug() << "processImageData threadID" << QThread::currentThreadId();
//        return;
//    }
    
    //QMutexLocker locker(&m_mutex);
    if (!m_frame || !m_codecCtx) {
        emit errorOccurred("编码器未初始化");
        return;
    }
    
    // 确保帧数据可写
    if (av_frame_make_writable(m_frame) < 0) {
        emit errorOccurred("无法获取帧的写入权限");
        return;
    }
    
    // 转换16位灰度图到YUV420P格式
    if (!convertToYUV420P(data, m_frame)) {
        emit errorOccurred("转换图像格式失败");
        return;
    }
    
    // 编码帧
    QByteArray encodedData;
    if (encodeFrame(m_frame, encodedData) && !encodedData.isEmpty()) {
        emit frameEncoded(encodedData);
    }
}

void ImageProcessor::onInitEncoder()
{
    // 初始化软件编码器
//    if (!initEncoder(m_width, m_height, m_fps)) {
//        emit errorOccurred("编码器初始化失败");
//    }

    //初始化硬件编码器
    if (!initHardwareEncoder_h264_nvenc()) {
        emit errorOccurred("编码器初始化失败");
    }

    //初始化硬件编码器   feiteng x100 kylin 服务器
//    if (!initHardwareEncoder_h264_vaapi()) {
//        emit errorOccurred("编码器初始化失败");
//    }
}
